package cn.xd.web.adivce;

import cn.xd.common.constant.AuthConstant;
import cn.xd.common.enums.common.ResultCodeEnum;
import cn.xd.common.exception.BizException;
import cn.xd.common.exception.BizObjectNotFoundException;
import cn.xd.common.response.ResponseResult;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import jakarta.servlet.ServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.ContentCachingRequestWrapper;

/**
 * @author : mirzaLin
 * @data : 2023/11/19
 * @description : 异常处理器
 **/
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {

    /**
     * 获得缓存请求对象
     *
     * @param req
     * @return
     */
    private ContentCachingRequestWrapper getContentCachingRequestWrapper(HttpServletRequest req) {
        ContentCachingRequestWrapper requestWrapper = null;
        try {
            ServletRequestWrapper servletRequestWrapper = (ServletRequestWrapper) req;
            requestWrapper = (ContentCachingRequestWrapper) servletRequestWrapper.getRequest();
        } catch (Exception e) {
            requestWrapper = (ContentCachingRequestWrapper) req;
        }
        return requestWrapper;
    }

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseResult<?> methodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException e) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().getFirst();
        log.error("请求地址:[{}],请求参数:[{}],错误:[{}]", req.getRequestURI()

                , JSON.toJSONString(e.getBindingResult().getTarget(), JSONWriter.Feature.WriteNullStringAsEmpty)
                , objectError.getDefaultMessage());
        // 然后提取错误提示信息进行返回
        return ResponseResult.failed(objectError.getDefaultMessage(), ResultCodeEnum.PARAM_ERROR);
    }

    @ExceptionHandler({BizException.class, BizObjectNotFoundException.class})
    public ResponseResult<?> bizExceptionHandler(HttpServletRequest req, BizException e) {
        /*
         * 需要配置过滤器，否则报错
         */
        ContentCachingRequestWrapper request = getContentCachingRequestWrapper(req);
        byte[] contentAsByteArray = request.getContentAsByteArray();
        String message = e.getMessage();
        String requestURI = req.getRequestURI();
        String token = request.getHeader(AuthConstant.JWT_TOKEN_HEADER);

        // 然后提取错误提示信息进行返回
        try {
            log.warn("请求地址:[{}],token:[{}],请求参数:[{}],错误:[{}],位置:[{}]", requestURI, token
                    , JSON.toJSONString(JSON.parse(IOUtils.toString(contentAsByteArray, request.getCharacterEncoding())))
                    , message, e.getStackTrace()[0]);
        } catch (Exception ex) {
            log.error("请求地址:[{}],token:[{}],请求参数:[读取失败],错误:[{}],位置:[{}]", requestURI, token
                    , message, e.getStackTrace()[0], e);
        }
        return ResponseResult.failed(message, ResultCodeEnum.BIZ_ERROR);
    }

    @ExceptionHandler(Exception.class)
    public ResponseResult<?> exceptionHandler(HttpServletRequest req, Exception e) {
        /*
         * 需要配置过滤器，否则报错
         */
        ContentCachingRequestWrapper request = getContentCachingRequestWrapper(req);
        String token = request.getHeader(AuthConstant.JWT_TOKEN_HEADER);
        byte[] contentAsByteArray = request.getContentAsByteArray();
        String message = e.getMessage();
        String requestURI = req.getRequestURI();
        // 然后提取错误提示信息进行返回
        try {
            log.error("请求地址:[{}],token:[{}],请求参数:[{}],错误:[{}],位置:[{}]", requestURI, token
                    , JSON.toJSONString(JSON.parse(IOUtils.toString(contentAsByteArray, request.getCharacterEncoding())))
                    , message, e.getStackTrace()[0], e);
        } catch (Exception ex) {
            log.error("请求地址:[{}],token:[{}],请求参数:[读取失败],错误:[{}],位置:[{}]", requestURI, token
                    , message, e.getStackTrace()[0], e);
        }

        return ResponseResult.failed(String.format(ResultCodeEnum.UNDEFINED_ERROR.getInfo(), e), ResultCodeEnum.UNDEFINED_ERROR);
    }

}
